package a5.environment;

import java.util.Map;

import a5.function.Function;

import com.google.inject.ImplementedBy;

/**
 * <pre>
 * An interface designed to model an environment in which computer programs are executed.
 * Namely, this interface standardizes mechanisms for defining variable and function names
 * as well as bindng them to a definition. The implementation details on scoping are left to 
 * the implementing class.
 * 
 * Additionally, the methods {@link #getConstantVariables()} and {@link #getConstantFunctions()} are to provide access to variables and functions
 * which are to be loaded on the instantiation of the Environment. The name "constant" is a bit of a misnomer since there may be no mechanism to prevent replacement
 * of the function or variable name.
 * 
 *  The {@code @ImplementedBy} annotation defines the default implementation for this interface for an injector.
 * </pre>
 * 
 * @author Jason Campos
 * @version Assignment 6, CS 152 (Programming Paradigms) Fall 2012, Jeffrey Smith
 */
@ImplementedBy(EnvironmentImpl.class)
public interface Environment{
	/**
	 * Saves a variable to the environment. A subsequent request to {@link #getVariable(String)} using the same {@code variableName} will return the
	 * value of the variable at the time of invocation. This may or may not be the same as the value set during {@link #setVariable(String, String)}. 
	 * It is up to the implementation to determine scope and overwriting policies. 
	 * 
	 * @param variableName
	 * The key by which the variable may later be dereferenced.
	 * @param value
	 * The value to assign to the argument {@code variableName}
	 * @return
	 * The value of the variable saved to the environment.
	 */
	public String setVariable(String variableName, String value);
	
	/**
	 * Dereferences the value of a variable in the environment saved as {@code variableName}.
	 * @param variableName
	 * The name of the variable to dereference
	 * @return
	 * The value (at the time of invocation) of the variable associated with the environment
	 * @throws IllegalArgumentException
	 * Thrown when no variable exists within the environment by the name {@code variableName}
	 */
	public String getVariable(String variableName) throws IllegalArgumentException;
	
	/**
	 * Saves a {@link a5.function.Function Function} to the environment.
	 * A subsequent request to {@link #getFunction(String)} using the same {@code functionName} will return the
	 * {@code Function} associated to the name at the time of invocation. This may or may not be the 
	 * same {@code Function} set during {@link #setFunction(String, Function)}. 
	 * It is up to the implementation to determine scope and overwriting policies. 
	 * 
	 * @param functionName
	 * The key by which the {@link a5.function.Function Function} may later be dereferenced.
	 * @param function
	 * The {@link a5.function.Function Function} to assign to the argument {@code functionName}
	 * @return
	 * The {@link a5.function.Function Function} saved to the environment
	 */
	public Function setFunction(String functionName, Function function);
	
	/**
	 * Dereferences the {@link a5.function.Function Function} in the environment saved as {@code functionName}.
	 * @param functionName
	 * The name of the {@code Function} to dereference
	 * @return
	 * The {@code Function} (at the time of invocation) associated with the environment with name {@code functionName}
	 * @throws IllegalArgumentException
	 * Thrown when no {@code Function} exists within the environment with the argument {@code functionName}
	 */
	public Function getFunction(String functionName) throws IllegalArgumentException;
	
	/**
	 * @return
	 * A read-only copy of variables loaded into the environment at instantiaton. Should 
	 * NOT return the current value of the variables but rather the value provided at instantiation.
	 */
	public Map<String,String> getConstantVariables();
	
	/**
	 * @return
	 * A read-only copy of {@link com.environment.function.Function Function}s loaded into the 
	 * environment at instantiation. Should NOT return the current functions associated with
	 * the function names but rather the {@code Function}s provided at instantiation. 
	 */
	public Map<String,Function> getConstantFunctions();
	
	/**
	 * Scope manipulation method. This method should be invoked when the interpreter should increase the depth of scope (for example,
	 * during function execution). The implementing class should document policies on how variables and functions are resolved from 
	 * a particular point of execution with regards to scope.   
	 * @return
	 * A reference to the in scope environment. 
	 */
	public Environment pushFrame(String envName);
	
	/**
	 * Scope manipulation method. This method should be invoked when the interpreter should increase the depth of scope (for example,
	 * during function execution). The implementing class should document policies on how variables and functions are resolved from 
	 * a particular point of execution with regards to scope.   
	 * @return
	 * A reference to the in scope environment. 
	 */
	public Environment popFrame();
}
